package org.marverse.universe.atoms

import scala.collection.mutable.{HashSet, HashMap}
import scala.collection.Map
import util.{MutableTypedMap, TypedKey}
import org.marverse.persistance.PId
import scripting.{Type}
import universe.{Universe, UniverseExtension, UniverseExtensionProvider}

object AtomType extends Type {
  override def memberDeclarations = Seq.empty
  override def isInstance(v: Any) = v.isInstanceOf[Atom]
}

final class Atom private (val pid: PId) {
  def addToUniverse(transaction: UniverseTransaction) {
    transaction.append(Atoms.AddAtom(this))
  }
  def removeFromUniverse(transaction: UniverseTransaction) {
    transaction.append(Atoms.RemoveAtom(this))
  }
}

object Atom {
  def apply(pid: PId) = {
    assert(!Atoms.atomsCache.contains(pid))
    val result = new Atom(pid)
    Atoms.atomsCache += ((pid, result))
    result
  }
}

class Atoms private (universe: Universe) extends UniverseExtension {
  private def this(universe: Universe, previous: Atoms) {
    this(universe)
    atomPIds ++= previous.atomPIds
  }
  
  override def check(implicit ctx: MutableTypedMap[TypedKey, Any]) {
    // pass
  }
  
  private val atomPIds = new HashSet[PId]
  lazy val atoms: Map[PId, Atom] = new Map[PId, Atom] {
    def get(pid: PId) = if (atomPIds.contains(pid))
      Some(Atoms.atomsCache.getOrElseUpdate(pid, universe.persistentStore.load(pid, classOf[Atom])))
    else
      None
    def size = atomPIds.size
    def elements = atomPIds.elements map { pid => (pid, Atoms.atomsCache.getOrElseUpdate(pid, universe.persistentStore.load(pid, classOf[Atom]))) }
  }
  def advance(advancedUniverse: Universe) = new Atoms(advancedUniverse, this)
}

object Atoms extends UniverseExtensionProvider(classOf[Atoms]) {
  def createExtension(universe: Universe) = new Atoms(universe)

  private[atoms] val atomsCache = new HashMap[PId, Atom]()
  
  final case class AddAtom(val atom: Atom) extends UniverseTransition {
    def check(implicit ctx: MutableTypedMap[TypedKey, Any]) {
      assertThat(ctx(Universe)(Atoms).atoms.contains(atom.pid),
        "new atom (%s) for already exists".format(atom))
    }
    def perform(universe: Universe) {
      universe(Atoms).atomPIds += atom.pid
      //universe.persistentStore.store(atom.pid, atom)
    }
  }
  
  final case class RemoveAtom(val atom: Atom) extends UniverseTransition {
    def check(implicit ctx: MutableTypedMap[TypedKey, Any]) {
      assertThat(!ctx(Universe)(Atoms).atoms.contains(atom.pid),
        "atom (%s) does not exists".format(atom))
    }
    def perform(universe: Universe) {
      universe(Atoms).atomPIds -= atom.pid
      // universe.persistentStore.remove(atom.pid)
    }
  }  
  
}
